package org.mobicents.diameter.server.bootstrap;

	
	import java.lang.reflect.InvocationHandler;
	import java.lang.reflect.Method;
	import java.lang.reflect.Proxy;
	import java.lang.reflect.UndeclaredThrowableException;
	import java.util.Hashtable;
	import java.util.ArrayList;
import java.util.Properties;
	import java.io.BufferedReader;
	import java.io.InputStreamReader;
	import javax.management.MBeanServerConnection;
	import javax.management.ObjectName;
	import javax.naming.Context;
	import javax.naming.InitialContext;


	import gnu.getopt.Getopt;
	import gnu.getopt.LongOpt;


	import org.jboss.security.SecurityAssociation;
	import org.jboss.security.SimplePrincipal;
import org.jnp.interfaces.NamingContext;

	 
	
	/**
	 * Based on org.jboss.Shutdown by S.Stark and J. Dillon
	 * @author baranowb
	 */
	public class Shutdown
	{
	   /////////////////////////////////////////////////////////////////////////
	   //                         Command Line Support                        //
	   /////////////////////////////////////////////////////////////////////////
	 
	   
	   public static final String PROGRAM_NAME = System.getProperty("program.name", "shutdown");
	   
	   protected static void displayUsage()
	   {
	      System.out.println("A JMX client to shutdown (exit or halt) a remote JBoss server.");
	      System.out.println();
	      System.out.println("usage: " + PROGRAM_NAME + " [options] <operation>");
	      System.out.println();
	      System.out.println("options:");
	      System.out.println("    -h, --help                Show this help message (default)");
	      System.out.println("    -D<name>[=<value>]        Set a system property");
	      System.out.println("    --                        Stop processing options");
	      System.out.println("    -s, --server=<url>        Specify the JNDI URL of the remote server");
	      System.out.println("    -a, --adapter=<name>      Specify JNDI name of the MBeanServerConnection to use");
	      System.out.println("    -u, --user=<name>         Specify the username for authentication");
	      System.out.println("    -p, --password=<name>     Specify the password for authentication");
	      
	   }

	   public static void main(final String[] args) throws Exception
	   {
	      if (args.length == 0)
	      {
	         displayUsage();
	         System.exit(0);
	      }
	      
	      ObjectName serverJMXName =  new ObjectName("mobicents:service=ContainerStopper");
	      
	      String sopts = "-:hD:s:a:u:p:";
	      LongOpt[] lopts =
	      {
	         new LongOpt("help", LongOpt.NO_ARGUMENT, null, 'h'),
	         new LongOpt("server", LongOpt.REQUIRED_ARGUMENT, null, 's'),
	         new LongOpt("adapter", LongOpt.REQUIRED_ARGUMENT, null, 'a'),
	         new LongOpt("user", LongOpt.REQUIRED_ARGUMENT, null, 'u'),
	         new LongOpt("password", LongOpt.REQUIRED_ARGUMENT, null, 'p'),
	      };

	      Getopt getopt = new Getopt(PROGRAM_NAME, args, sopts, lopts);
	      int code;
	      String arg;

	      String serverURL = null;
	      String adapterName = "jmx/rmi/RMIAdaptor";
	      String username = null;
	      String password = null;
	     
	      boolean exit = false;
	      boolean halt = false;
	      int exitcode = -1;

	      while ((code = getopt.getopt()) != -1)
	      {
	         switch (code)
	         {
	            case ':':
	            case '?':
	               // for now both of these should exit with error status
	               System.exit(1);
	               break;

	            case 1:
	               // this will catch non-option arguments
	               // (which we don't currently care about)
	               System.err.println(PROGRAM_NAME + ": unused non-option argument: " +
	               getopt.getOptarg());
	               break;
	            case 'h':
	               displayUsage();
	               System.exit(0);
	               break;
	            case 'D':
	            {
	               // set a system property
	               arg = getopt.getOptarg();
	               String name, value;
	               int i = arg.indexOf("=");
	               if (i == -1)
	               {
	                  name = arg;
	                  value = "true";
	               }
	               else
	               {
	                  name = arg.substring(0, i);
	                  value = arg.substring(i + 1, arg.length());
	               }
	               System.setProperty(name, value);
	               break;
	            }
	            case 's':
	               serverURL = getopt.getOptarg();
	               break;
	            case 'S':
	               // nothing...
	               break;
	            case 'a':
	               adapterName = getopt.getOptarg();
	               break;
	            case 'u':
	               username = getopt.getOptarg();
	               SecurityAssociation.setPrincipal(new SimplePrincipal(username));
	               break;
	            case 'p':
	               password = getopt.getOptarg();
	               SecurityAssociation.setCredential(password);
	               break;

	         }
	      }
	      
	      InitialContext ctx;

	      // If there was a username specified, but no password prompt for it
	      if( username != null && password == null )
	      {
	         System.out.print("Enter password for "+username+": ");
	         BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	         password = br.readLine();
	         SecurityAssociation.setCredential(password);
	      }

	      if (serverURL == null)
	      {
	    	  Properties props = new Properties();
	    	  props.setProperty( "java.naming.factory.initial",
	    	  "org.jnp.interfaces.NamingContextFactory" );
	    	  props.setProperty( "java.naming.provider.url", "127.0.0.1:1099" );
	    	  props.setProperty( "java.naming.factory.url.pkgs", "org.jboss.naming" );
	         ctx = new InitialContext(props);
	      }
	      else
	      {
	    	  //not tested
	    	  Properties props = new Properties();
	    	  props.setProperty( "java.naming.factory.initial",
	    	  "org.jnp.interfaces.NamingContextFactory" );
	    	  props.setProperty( "java.naming.provider.url", serverURL );
	    	  props.setProperty( "java.naming.factory.url.pkgs", "org.jboss.naming" );
	         
	    	  props.put(NamingContext.JNP_DISABLE_DISCOVERY, "true");
	         ctx  = new InitialContext(props);
	      }

	      Object obj = ctx.lookup(adapterName);
	      if (!(obj instanceof MBeanServerConnection))
	      {
	         throw new RuntimeException("Object not of type: MBeanServerConnection, but: " +
	            (obj == null ? "not found" : obj.getClass().getName()));
	      }

	      MBeanServerConnection adaptor = (MBeanServerConnection) obj;
	      ServerProxyHandler handler = new ServerProxyHandler(adaptor, serverJMXName);
	      Class[] ifaces = {ContainerOperations.class};
	      ClassLoader tcl = Thread.currentThread().getContextClassLoader();
	      ContainerOperations server = (ContainerOperations) Proxy.newProxyInstance(tcl, ifaces, handler);

	      server.terminateContainer();
	      System.out.println("Shutdown message has been posted to the server.");
	      System.out.println("Server shutdown may take a while - check logfiles for completion");
	   }

	   private static class ServerProxyHandler implements InvocationHandler
	   {
	      ObjectName serverName;
	      MBeanServerConnection server;
	      ServerProxyHandler(MBeanServerConnection server, ObjectName serverName)
	      {
	         this.server = server;
	         this.serverName = serverName;
	      }

	      public Object invoke(Object proxy, Method method, Object[] args)
	            throws Throwable
	      {
	         String methodName = method.getName();
	         Class[] sigTypes = method.getParameterTypes();
	         ArrayList sigStrings = new ArrayList();
	         for(int s = 0; s < sigTypes.length; s ++)
	            sigStrings.add(sigTypes[s].getName());
	         String[] sig = new String[sigTypes.length];
	         sigStrings.toArray(sig);
	         Object value = null;
	         try
	         {
	            value = server.invoke(serverName, methodName, args, sig);
	         }
	         catch(UndeclaredThrowableException e)
	         {
	            System.out.println("getUndeclaredThrowable: "+e.getUndeclaredThrowable());
	            throw e.getUndeclaredThrowable();
	         }
	         return value;
	      }
	   }
	}
